Skip to content

fix(auth): normalize bearer token whitespace for rate limit stability - #1291

Merged
raullenchai merged 1 commit into
raullenchai:mainfrom
2005rishabh:fix/bearer-token-whitespace
Aug 2, 2026
Merged

fix(auth): normalize bearer token whitespace for rate limit stability#1291
raullenchai merged 1 commit into
raullenchai:mainfrom
2005rishabh:fix/bearer-token-whitespace

Conversation

@2005rishabh

@2005rishabh 2005rishabh commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • Adds .strip() to _extract_bearer_token in vllm_mlx/middleware/auth.py so surrounding whitespace (spaces, tabs, newlines) is normalized and an all-whitespace token is treated as absent.
  • Adds tests in tests/test_config_and_middleware.py: the helper's whitespace edge cases and a bucket-level contract test asserting auth-equivalent Bearer values resolve to one _rate_limit_client_id.

Why is this needed?

Closes the "Bearer token whitespace normalization" task from #337.
Without this, a client can dodge its own rate limit just by padding the Bearer token with extra spaces/tabs — the unstripped string hashes to a brand-new bucket identity.

Scope note (maintainer, narrowed for merge)

Rebased onto current main and narrowed to the auth fix + tests per review. Dropped two out-of-scope changes from the original revision: the if TYPE_CHECKING: gate on server_config.py (it broke runtime typing.get_type_hints(ServerConfig) with NameError: BaseEngine), and three except ImportError: pytest.skip guards on the exception-handler tests (they'd turn a broken/partial MLX install into a green skip and hide packaging regressions). Original authorship preserved on the commit.

Test plan

  • python3.12 -m pytest tests/test_config_and_middleware.py::TestRateLimitClientId -q — 5 passed.
  • ruff check && ruff format --check on both touched files — clean.

Checklist

  • Tests pass locally
  • Lint passes (ruff check && ruff format --check)
  • Self-validated via pr_validate
  • New tests touch a security path — the bucket contract test fails if the .strip() is reverted
  • Updated README/docs if applicable — N/A (internal auth helper, no user-facing surface)
  • No breaking changes to existing API

Copy link
Copy Markdown
Owner

Thanks for taking this on. The PR is necessary: it addresses the explicitly assigned Bearer-token whitespace hardening item from #337, and the core token.strip(); return token or None change correctly makes FastAPI-auth-equivalent Bearer values map to the same rate-limit identity.

I do not think the current revision is merge-ready yet:

  1. Blocking — runtime annotation regression. Moving BaseEngine behind TYPE_CHECKING in vllm_mlx/config/server_config.py breaks runtime annotation resolution. On this branch, typing.get_type_hints(ServerConfig) raises NameError: name 'BaseEngine' is not defined; the same call succeeds on current main. This change is unrelated to the auth fix and should be removed (or redesigned in a separate PR with a runtime-resolvable annotation).

  2. Blocking — overly broad test skips. The three new except ImportError: pytest.skip(...) blocks can turn a broken or partially installed MLX package, including a missing transitive dependency, into a green skip. That conceals packaging regressions in existing exception-handler tests. These changes are also outside the Bearer-normalization scope and should be removed. If cross-platform availability needs separate work, it should use a narrow availability check in a dedicated PR.

  3. Test gap — pin the security contract, not only the helper. The new helper assertions are useful, but the regression being fixed is rate-limit bucket fragmentation. Please add/replace with a contract test using _rate_limit_client_id(Request(...)) and assert that these auth-equivalent values produce the same client ID:

    • Bearer test-secret
    • Bearer test-secret
    • Bearer test-secret
    • Bearer \t test-secret \n

    I verified locally that the proposed production change satisfies this bucket-level contract.

  4. The branch is currently 14 commits behind main and has no reported GitHub checks. Please rebase after narrowing the diff and rerun the normal validation.

  5. pr_validate also rejects the unchecked Updated README/docs if applicable checklist item. If it is not applicable, remove it or mark it explicitly complete/N/A.

Suggested minimal scope: vllm_mlx/middleware/auth.py plus the focused rate-limit bucket regression test. With the unrelated config/test-skip changes removed, the core fix looks sound.

@raullenchai
raullenchai force-pushed the fix/bearer-token-whitespace branch from bc8c2c4 to d0a45a3 Compare August 2, 2026 14:41
@raullenchai

Copy link
Copy Markdown
Owner

Maintainer update — took this to merge-ready via maintainerCanModify (thanks @2005rishabh, your commit authorship is preserved).

Rebased onto current main (the branch was ~pre-audio/video-refactor and diffed noisily) and narrowed to exactly the sound part:

  • Kept: _extract_bearer_tokentoken = token.strip(); return token or None.
  • Added: a bucket-level contract test (_rate_limit_client_id on Bearer test-secret vs whitespace-padded variants → same client id), alongside your helper edge-case test. This pins the actual security regression (bucket fragmentation), not just the helper.
  • Dropped (out of scope, both flagged in review): the TYPE_CHECKING gate on server_config.py (it broke runtime typing.get_type_hints(ServerConfig)NameError: BaseEngine), and the three except ImportError: pytest.skip guards (a broken/partial MLX install would silently green-skip and hide packaging regressions).

Diff is now auth.py (+2/-1) + test_config_and_middleware.py (+41). Local: TestRateLimitClientId 5 passed, ruff clean. Running codex + pr_validate; will squash-merge on green.

`_extract_bearer_token` returned the raw post-scheme remainder, so the
same secret sent as `Bearer sk-abc`, `Bearer  sk-abc`, or `Bearer sk-abc `
hashed to three different rate-limit buckets — a client could dodge its
own limit just by padding the header, and FastAPI-auth-equivalent values
mapped to distinct identities. Strip surrounding whitespace (spaces, tabs,
newlines) and treat an all-whitespace token as absent.

Pins the bucket-level security contract (auth-equivalent Bearer values →
one client id), not only the helper. Assigned Bearer-hardening item from
Auth follow-ups (raullenchai#337).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@raullenchai
raullenchai force-pushed the fix/bearer-token-whitespace branch from d0a45a3 to 6d87471 Compare August 2, 2026 14:45
@raullenchai
raullenchai merged commit 4d6fe3e into raullenchai:main Aug 2, 2026
@2005rishabh

Copy link
Copy Markdown
Contributor Author

Thank you for stepping in to refine the scope and correct the TYPE_CHECKING gate issue. I appreciate the clear feedback and the merge.

@2005rishabh
2005rishabh deleted the fix/bearer-token-whitespace branch August 2, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants